<?php
namespace Bundle\ModuleBundle;

use Bundle\ModuleBundle\Interfaces\ModuleManagerInterface;

use Symfony\Components\DependencyInjection\ContainerInterface;
use Symfony\Component\Finder\Finder;

class ModuleManager implements ModuleManagerInterface
{
    /**
     * Контейнер настроек
     * @var Symfony\Components\DependencyInjection\ContainerInterface
     */
    private $_container;

    private $_modulesSearchPath = array();

    private $_pluginsSearchPath = array();

    private $_modules = array();
    private $_plugins = array();

    private $_isLoaded = false;

    /** Constants */
    private $_pluginDescriptionClass = 'Bundle\\ModuleBundle\\Interfaces\\PluginDescriptionInterface';

    public $loaded = false;
    
    public function __construct($container)
    {
        $this->_container = $container;
    }

    public function offsetUnset($offset)
    {
        throw new Exception(sprintf("Not implemented %s:%d | %s::%s", __FILE__, __LINE__, __CLASS__, __METHOD__));
    }

    public function offsetSet($offset, $value)
    {
        throw new Exception(sprintf("Not implemented %s:%d | %s::%s", __FILE__, __LINE__, __CLASS__, __METHOD__));
    }

    public function offsetGet($offset)
    {
        return $this->getModule($offset);
    }

    public function offsetExists($offset)
    {
        return $this->hasModule($offset);
    }

    public function hasPlugin($plugin)
    {
        return \array_key_exists($plugin, $this->_plugins);
    }

    public function getModule($module)
    {
        if(!$this->_isLoaded)
        {
            $this->load();
        }
        if($this->hasModule($module))
        {
            return new Module($this, $this->_modules[$module] . '/config.yml', $module);
        }
        throw new \Exception(sprintf("Module with name `%s` not found.", $module));
    }

    public function hasModule($module)
    {
        if(!$this->_isLoaded)
        {
            $this->load();
        }
        return array_key_exists($module, $this->_modules);
    }

    public function getModules()
    {
        if(!$this->_isLoaded)
        {
            $this->load();
        }
        return array_keys($this->_modules);
    }

    public function getPlugins()
    {
        if(!$this->_isLoaded)
        {
            $this->load();
        }
        return array_keys($this->_plugins);
    }

    public function setPluginsSearchPath($path)
    {
        $this->_pluginsSearchPath = array_unique(array_merge(
            $this->_pluginsSearchPath, (array) $path
        ));

        $this->_isLoaded = false;
    }

    public function setModulesSearchPath($path)
    {
        $this->_modulesSearchPath = array_unique(array_merge(
            $this->_modulesSearchPath, (array) $path
        ));

        $this->_isLoaded = false;
    }

    public function getLogger()
    {
    	return $this->_container->get('logger');
    }
    
    public function load()
    {
        if(count($this->_modulesSearchPath) == 0)
        {
            throw new \Exception("No modules search pathes are set!");
        }

        if(count($this->_pluginsSearchPath) == 0)
        {
            throw new \Exception("No plugins search pathes are set!");
        }

        //@TODO: mb wrong!;

        $this->_modules = array();
        $this->_plugins = array();

        $this->loadPlugins();
        $this->loadModules();

        $this->_isLoaded = true;
    }

    public function getPlugin($plugin)
    {
        if($this->hasPlugin($plugin))
        {
            return $this->_plugins[$plugin];
        }
        throw new Exception(sprintf("Not implemented %s:%d | %s::%s", __FILE__, __LINE__, __CLASS__, __METHOD__));
    }

    private function loadPlugins()
    {
        $rawList = array();

        foreach($this->_pluginsSearchPath as $namespace => $path)
        {
            $finder = new Finder();
            $finder->files()->in($path)->name("PluginDescription.php");

            foreach($finder as $pd)
            {
                $rawList[$namespace] [] = $pd->getPath();
            }
        }

        foreach($rawList as $namespace => $descriptionFiles)
        {
            foreach($descriptionFiles as $descriptionFile)
            {
                $els = \explode('\\', \str_replace('/', '\\', $descriptionFile));
                $className = $namespace . $els[\count($els) - 1] . '\\PluginDescription';
                
                $reflection = new \ReflectionClass($className);

                if($reflection->isSubclassOf($this->_pluginDescriptionClass) &&
                    !$reflection->isAbstract())
                {
                    $description = $reflection->newInstance();

                    if($description->boot())
                    {
                        $names = array_merge((array) $description->getName(), $description->getAliases());

                        foreach($names as $name)
                        {
                            if(array_key_exists($name, $this->_plugins))
                            {
                             //   throw new \Exception(sprintf('Plugin with name `%s` already exists.', $name));
                                //@TODO BAD!!! VERY BAD!!!
                            }
                            $this->_plugins[$name] = $description;
                        }
                    }
                }
                else
                {
                    throw new \Exception(sprintf('Plugin `%s` is not correct.', $className));
                }
            }
        }
    }

    private function loadModules()
    {
        $finder = new Finder();
        $finder->directories()->in($this->_modulesSearchPath);

        foreach($finder as $dir)
        {
            $this->_modules[$dir->getFilename()] = $dir->getRealPath();
        }
    }

    /**
     * Ultimate FAIL!
     * @return Symfony\Components\DependencyInjection\ContainerInterface
     */
    public function getContainer()
    {
        return $this->_container;
    }

    public function getModulesSearchPath()
    {
        return $this->_modulesSearchPath;
    }

    public function getEntityClassNameTemplate($moduleName)
    {
        return sprintf("%s\\%sModuleEntity",
            $this->_container->getParameter('module.namespace_for_models'), $moduleName
        );
    }

    public function getEntityManager()
    {
        return $this->_container->getDoctrine_ORM_EntityManagerService();
    }
    
    public function getNodeId()
    {
    	return $this->_container->get('fireice')->getCurrentNode();
    }
}
